package com.tuling.code.ratelimit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * 请求限流控制,使用 Guava 开发库，增加时间滑动窗口算法
 * 
 * @author lenovo
 *
 */
public class RateLimitTester {
	static Random rd = new Random();
	// 滑动周期缓存数据
	static final LoadingCache<Long, AtomicLong> counter = CacheBuilder.newBuilder().maximumSize(100)
			.expireAfterWrite(1, TimeUnit.SECONDS).build(new CacheLoader<Long, AtomicLong>() {
				@Override
				public AtomicLong load(Long seconds) throws Exception {
					return new AtomicLong(0);
				}

			});

	public static void main(String[] args) {
		long limit = 1000;

		ExecutorService executor = Executors.newFixedThreadPool(10);
		// 打印滑动窗口
		executor.submit(() -> {
			while (true) {
				sleep(1000);
				printTotal();
			}
		});
		
		// 并发测试
		for (int i = 0; i < 10; i++) {
			executor.submit(() -> {
				while (true) {
					//sleep((int) Math.random() * 1000);
					long currentSeconds = System.currentTimeMillis()/1000;
					if (counter.get(currentSeconds).getAndIncrement() > limit) {
						//System.out.println("限流了 : "+ currentSeconds);
						continue;
					}

					// 数量增加
					//counter.get(currentSeconds).incrementAndGet();
					//System.out.println("业务执行中...");
				}
			});
		}
	}

	/**
	 * 打印信息
	 */
	private static void printTotal() {
		ConcurrentMap<Long, AtomicLong> map = counter.asMap();
		StringBuilder total = new StringBuilder();
		Set<Long> longs = map.keySet();
		ArrayList<Long> ts = new ArrayList<>(longs);
		Collections.sort(ts);
		for (Long long1 : ts) {
			total.append(map.get(long1).get()).append(" , ");
		}
		System.out.println(total);

	}

	/*private static long getTotal() {
		ConcurrentMap<Long, AtomicLong> asMap = counter.asMap();
		int total = 0;
		for (AtomicLong l : asMap.values()) {
			total += l.get();
		}
		return total;
	}*/

	/**
	 * 休眠毫秒
	 * 
	 * @param time
	 */
	private static void sleep(long time) {
		try {
			Thread.sleep(time);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
